

using Tao.OpenGl;
using System.Threading;
using System;
using System.Windows.Forms;

namespace cs5643.rigidbody
{




    /**
     * 
     * CS5643: Assignment #3 "Rigid Body Contact" main
     * class. RigidImageSimulation simulates rigid bodies derived from an
     * image's connected nonwhite components.
     * 
     * @author Doug James, March 2007, 2009
     */
    public class RigidImageSimulation
    {
        /** Reference to current FrameExporter, or null if no frames being
         * dumped. */
        FrameExporter frameExporter;

        /** Number of time steps per large step size. */
        static int N_STEPS_PER_FRAME = 15;

        /** Size of symplectic Euler time step (in seconds). You may want
         * to change this, but watch out for interpenetrations at high
         * linear/angular speeds! */
        public static double DT = 0.0001;

        private int width, height;

        /** The single RigidBodySystem reference. */
        RigidBodySystem RBS;

        /** Toggle to advance simulation. */
        bool simulate = false;
        bool oneStep = false;

        /** Draws wireframe if true, and pixel blocks if false. */
        bool drawWireframe = false;

        /** Draws object bounds (Disk) if true. */
        bool drawBounds = false;

        /** If true, instead of one step per frame, simulator takes
         * N_STEPS_PER_FRAME. */
        bool largeStep = true;

        /** Non-null reference when simulating the "Jelly Bean Factory" images. */
        JellyBeanFactory jellyBeanFactory = null;

        /** 
         * Main constructor. Call start() to begin simulation. 
         * 
         * @param imageFilename Image to simulate.
         * @param processCollisions Enables collisions if true.
         */
        public RigidImageSimulation(String imageFilename, bool processCollisions)
        {
            RBS = new RigidBodySystem();
            RBS.setProcessCollisions(processCollisions);

            ImageBlocker blocker = new ImageBlocker(imageFilename);
            blocker.AddComponentRigidBodies(RBS);

            /// CONSTRAIN RIGID BODIES THAT ARE BLUE:
            foreach (RigidBody b in RBS.getRigidBodies())
            {
                if (b.hasAllBlueBlocks())
                    b.setPin(true);
            }

            //Console.Out.WriteLine("imageFilename = "+imageFilename);
            if (imageFilename.Contains("jellyBeanFactory"))
            { //// "OCD!" awaits
                jellyBeanFactory = new JellyBeanFactory(RBS);
            }
        }

        /**
         * Builds and shows window, and starts simulator.
         */
        public void start()
        {

            TaoForm.RIS = this;

            TaoForm.CreateGLWindow("CS5643 Rigid Body Contact!", 320, 320, 32);

            TaoForm.Run();

        }



        

        /** Maps mouse event into computational cell using OrthoMap. */
        public Pnt2D getPnt2D(MouseEventArgs e)
        {
            return TaoForm.orthoMap.getPnt2D(e);
        }

        /** GLEventListener implementation: Initializes JOGL renderer. */
        public void init()
        {
            // DEBUG PIPELINE (can use to provide GL error feedback... disable for speed)
            //drawable.setGL(new DebugGL(drawable.getGL()));

            Console.Out.WriteLine("INIT GL IS: something");

            //Gl.glSetSwapInterval(1);

            

            Gl.glLineWidth(1.0f);
            Gl.glPointSize(1.0f);

            Gl.glDisable(Gl.GL_DEPTH_TEST);

            Gl.glEnable(Gl.GL_BLEND);
            Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA);
            //Gl.glBlendFunc(Gl.GL_SRC_ALPHA_SATURATE, Gl.GL_ONE_MINUS_SRC_ALPHA);
            //Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE);
            Gl.glEnable(Gl.GL_POINT_SMOOTH);
            Gl.glHint(Gl.GL_POINT_SMOOTH_HINT, Gl.GL_NICEST);
            Gl.glEnable(Gl.GL_LINE_SMOOTH);
            Gl.glHint(Gl.GL_LINE_SMOOTH_HINT, Gl.GL_NICEST);
            Gl.glEnable(Gl.GL_POLYGON_SMOOTH);
            Gl.glHint(Gl.GL_POLYGON_SMOOTH_HINT, Gl.GL_NICEST);

        }

        /** GLEventListener implementation */
        public void displayChanged(bool modeChanged, bool deviceChanged) { }

        /** GLEventListener implementation */
        public void reshape(int x, int y, int width, int height)
        {
            Console.Out.WriteLine("width=" + width + ", height=" + height);
            height = Math.Max(height, 1); // avoid height=0;

            this.width = width;
            this.height = height;

            
            Gl.glViewport(0, 0, width, height);

            // SETUP ORTHOGRAPHIC PROJECTION AND MAPPING INTO UNIT CELL:
            Gl.glMatrixMode(Gl.GL_PROJECTION);
            Gl.glLoadIdentity();
            TaoForm.orthoMap = new OrthoMap(width, height);//Hide grungy details in OrthoMap
            TaoForm.orthoMap.apply_glOrtho();

            // GET READY TO DRAW:
            Gl.glMatrixMode(Gl.GL_MODELVIEW);
            Gl.glLoadIdentity();
        }

        int f;
        /** 
         * Main event loop: Gl display + simulation
         * advance. GLEventListener implementation.
         */
        public void display()
        {
             

            Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);
            Gl.glClearColor(1, 1, 1, 0);

            {/// DRAW COMPUTATIONAL CELL BOUNDARY:
                Gl.glBegin(Gl.GL_LINE_STRIP);
                if (simulate)
                    Gl.glColor4d(0.0, 0.0, 0.0, 1.0);
                else
                    Gl.glColor3d(1.0, 0.0, 0.0);
                Gl.glVertex3d(0, 0,0); 
                Gl.glVertex3d(1, 0,0); 
                Gl.glVertex3d(1, 1,0); 
                Gl.glVertex3d(0, 1,0); 
                Gl.glVertex3d(0, 0,0);
                Gl.glEnd();
            }

            if (drawWireframe) Gl.glPolygonMode(Gl.GL_FRONT, Gl.GL_LINE);
            else Gl.glPolygonMode(Gl.GL_FRONT, Gl.GL_FILL);

            simulateAndDisplayScene();/// <<<-- MAIN CALL
            Console.Out.WriteLine(f++);

            if (frameExporter != null) frameExporter.writeFrame();
        }

        /** Simulate then display particle system and any builder
         * adornments. */
        void simulateAndDisplayScene()
        {
            if (simulate)
            {/// TAKE DT step sizes... take N_STEPS_PER_FRAME if largeStep==true

                if (largeStep)
                {/// TAKE N_STEPS_PER_FRAME DT STEPS:
                    for (int k = 0; k < N_STEPS_PER_FRAME; k++)
                    {
                        advanceTime(DT);
                        if (oneStep)
                        {
                            simulate = !simulate;
                            oneStep = false;
                            break;
                        }
                    }
                }
                else
                {//JUST ONE DT STEP
                    advanceTime(DT);
                }

                

            }

            // Draw particles, springs, etc.
            RBS.display();

            if (drawBounds)
            {
                foreach (RigidBody body in RBS.getRigidBodies())
                {
                    body.displayBound();
                }
            }

            if (jellyBeanFactory != null)
            {
                Gl.glPolygonMode(Gl.GL_FRONT, Gl.GL_FILL);
                jellyBeanFactory.display( width, height);
                if (drawWireframe) Gl.glPolygonMode(Gl.GL_FRONT, Gl.GL_LINE);
            }
        }

        void advanceTime(double dt)
        {
            RBS.advanceTime(DT);

            if (jellyBeanFactory != null)
                jellyBeanFactory.advanceTime(dt);
        }

        private SpringForcePoint2Body mouseForce = null;

        // Methods required for the implementation of MouseListener

        public void mousePressed(MouseEventArgs e)
        {
            RBS.removeForce(mouseForce);//in case stale

            /// FIND A/CLOSEST BODY:
            Pnt2D p = getPnt2D(e);
            RigidBody body = RBS.pickBody(p);
            if (body != null)
            {
                Block b;
                if ((b = body.pickBlock(p)) != null)
                {
                    mouseForce = new SpringForcePoint2Body(body, b, p, RBS);
                    RBS.addForce(mouseForce);
                }
                else
                {
                    //System.out.println("MOUSE FORCE...");
                    mouseForce = new SpringForcePoint2Body(body, p, RBS);
                    RBS.addForce(mouseForce);
                }
            }
        }
        public void mouseReleased(MouseEventArgs e)
        {
            if (mouseForce != null) RBS.removeForce(mouseForce);
            mouseForce = null;
        }
        public void mouseClicked(MouseEventArgs e)
        {
            if (mouseForce != null) RBS.removeForce(mouseForce);
            mouseForce = null;
        }

        // Methods required for the implementation of MouseMotionListener
        public void mouseDragged(MouseEventArgs e)
        {
            if (mouseForce != null)
            {
                Pnt2D p = getPnt2D(e);
                mouseForce.updatePoint(p);
                //Console.Out.Write(" U ");
            }
        }

        /**
         * Handles keyboard events, e.g., spacebar toggles
         * simulation/pausing, and escape resets the current Task.
         */
        public void dispatchKey(char key, KeyPressEventArgs e)
        {
            //Console.Out.WriteLine("CHAR="+key+", keyCode="+e.getKeyCode()+", e="+e);
            if (key == ' ')
            {//SPACEBAR --> TOGGLE SIMULATE
                simulate = !simulate;
                if (simulate)
                {
                    Console.Out.WriteLine("GO!");
                }
                else
                {
                    Console.Out.WriteLine("STOP!");
                }
            }
            else if (key == 's')
            {// s == one step
                if (!simulate && !oneStep)
                {
                    simulate = true;
                    oneStep = true;
                    Console.Out.WriteLine("GO - one step!");
                }
            }
            else if (key == 'r')
            {//RESET
                Console.Out.WriteLine("RESET!");
                simulate = false;
                frameExporter = null;
                RBS.reset();
                if (jellyBeanFactory != null) jellyBeanFactory.reset();
            }
            else if (key == 'w')
            {//RESET
                //Console.Out.WriteLine("WIREFRAME");
                drawWireframe = !drawWireframe;
            }
            else if (key == 'b')
            {//RESET
                drawBounds = !drawBounds;
                //Console.Out.WriteLine("drawBounds = "+drawBounds);
            }
            else if (key == 'l')
            {//LARGE STEP TOGGLE
                largeStep = !largeStep;
                if (largeStep)
                    Console.Out.WriteLine("MULTI-STEP");
                else
                    Console.Out.WriteLine("SINGLE-STEP");
            }
            else if (key == 'j')
            {//RANDOM JIGGLES
                Console.Out.WriteLine("JIGGLING");
                RBS.jiggleON();
            }
            else if (e.ToString().Contains("Escape"))
            {//sloth
                //Console.Out.WriteLine("ESCAPE");
            }
            else if (key == 'e')
            {//toggle exporter
                frameExporter = ((frameExporter == null) ? (new FrameExporter()) : null);
                Console.Out.WriteLine("'e' : frameExporter = " + frameExporter);
            }
        }


        /**
         * Code to dump frames---very useful for slow/large runs. 
         */
        private static int exportId = -1;
        private class FrameExporter
        {
            private int nFrames = 0;

            public FrameExporter()
            {
                exportId += 1;
            }

            public void writeFrame()
            {
                //long   timeNS   = -System.DateTime.Now.Ticks;
                //String number   = Utils.getPaddedNumber(nFrames, 5, "0");
                //String filename = "frames/export"+exportId+"-"+number+".png";/// BUG: DIRECTORY MUST EXIST!

                //try{  
                //java.io.File   file     = new java.io.File(filename);
                //if(file.exists()) Console.Out.WriteLine("WARNING: OVERWRITING PREVIOUS FILE: "+filename);

                ///// WRITE IMAGE: ( :P Screenshot asks for width/height --> cache in GLEventListener.reshape() impl)
                //com.sun.openGl.glutil.Screenshot.writeToFile(file, width, height);

                //timeNS += System.nanoTime();
                //Console.Out.WriteLine((timeNS/1000000)+"ms:  Wrote image: "+filename);

                //}catch(Exception e) { 
                //e.printStackTrace();
                //Console.Out.WriteLine("OOPS: "+e); 
                //} 

                //nFrames += 1;
            }
        }



    }
}
